using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using MIMPGameLibrary;

namespace MIMP
{
    /// <summary>
    /// The class implementing IGameplayScreenService draws the Hexes and the Monsters.
    /// </summary>
    public interface IGameplayScreenService
    {
        event EventHandler HexLeftClicked;
        event EventHandler HexRightClicked;
        event EventHandler UIButtonLeftClicked;
        event EventHandler UIButtonRightClicked;
    }
    /// <summary>
    /// An extension of EventArgs intended for use with IGamePlayService's ButtonLeftClicked 
    /// and ButtonRightClicked events. Specifies the button clicked through use of ControlPanelButtons enum.
    /// </summary>
    public class UIButtonClickEventArgs : EventArgs
    {
        public UIButtonClickEventArgs(UIButtons button, ConsoleModifiers modifiers)
        {
            this.clickedButton = button;
            this.mods = modifiers;
        }
        private UIButtons clickedButton;
        /// <summary>
        /// The Hex that the user clicked on.
        /// </summary>
        public UIButtons ClickedButton
        {
            get { return clickedButton; }
        }
        private ConsoleModifiers mods;
        /// <summary>
        /// The modifier keys that were held down while the UIButton was clicked.
        /// </summary>
        public ConsoleModifiers Modifiers
        {
            get { return mods; }
        }
        /// <summary>
        /// True if shift was held down while clicking, false otherwise.
        /// </summary>
        public bool IsShiftDown
        {
            get { return (mods & ConsoleModifiers.Shift) != 0; }
        }
        /// <summary>
        /// True if control was held down while clicking, false otherwise.
        /// </summary>
        public bool IsCtrlDown
        {
            get { return (mods & ConsoleModifiers.Control) != 0; }
        }
        /// <summary>
        /// True if alt was held down while clicking, false otherwise.
        /// </summary>
        public bool IsAltDown
        {
            get { return (mods & ConsoleModifiers.Alt) != 0; }
        }
    }
    /// <summary>
    /// An extension of EventArgs intended for use with IGameplayScreenService's HexLeftClicked 
    /// and HexRightClicked events. Holds a reference to the Hex which was clicked on.
    /// </summary>
    public class HexClickEventArgs : EventArgs
    {
        public HexClickEventArgs(Hex clickedHex, ConsoleModifiers modifiers)
        {
            this.clickedHex = clickedHex;
            this.mods = modifiers;
        }
        private Hex clickedHex;
        /// <summary>
        /// The Hex that the user clicked on.
        /// </summary>
        public Hex ClickedHex
        {
            get { return clickedHex; }
        }
        private ConsoleModifiers mods;
        /// <summary>
        /// The modifier keys that were held down while the Hex was clicked.
        /// </summary>
        public ConsoleModifiers Modifiers
        {
            get { return mods; }
        }
        /// <summary>
        /// True if shift was held down while clicking, false otherwise.
        /// </summary>
        public bool IsShiftDown
        {
            get { return (mods & ConsoleModifiers.Shift) != 0; }
        }
        /// <summary>
        /// True if control was held down while clicking, false otherwise.
        /// </summary>
        public bool IsCtrlDown
        {
            get { return (mods & ConsoleModifiers.Control) != 0; }
        }
        /// <summary>
        /// True if alt was held down while clicking, false otherwise.
        /// </summary>
        public bool IsAltDown
        {
            get { return (mods & ConsoleModifiers.Alt) != 0; }
        }
    }
    /// <summary>
    /// This piece of the GameplayScreen class represents the hex map, which takes up the majority of the screen. 
    /// Together with the control panel, it comprises the main game screen. This component's job is to: 1) Draw 
    /// the hexes and the monsters, and 2) Fire an event when the user clicks on a Hex.
    /// </summary>
    public partial class GameplayScreen : GameScreen, IGameplayScreenService
    {
        public event EventHandler HexLeftClicked;
        public event EventHandler HexRightClicked;
        public event EventHandler UIButtonLeftClicked;
        public event EventHandler UIButtonRightClicked;

        Game game;
        GameState gameState;

        List<HexDisplay> HexDisplays;
        List<MonsterDisplay> MonsterDisplays;

        Texture2D background;
        Texture2D hexoutline;
        Texture2D hexshadow;
        Texture2D flag;

        IGameStateService gsService;

        Rectangle gameScreenSize;

        UIButtonDisplay ScrollUpButton;
        UIButtonDisplay ScrollDownButton;
        UIButtonDisplay ScrollLeftButton;
        UIButtonDisplay ScrollRightButton;

        PopupBox infoBox;

        public GameplayScreen(Game game)
            : base()
        {
            this.game = game;
            HexDisplays = new List<HexDisplay>();
            MonsterDisplays = new List<MonsterDisplay>();
            infoBox = new PopupBox("blah", new Vector2(100,100));
            //see ControlPanel.cs
            ControlPanelConstructor();
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            gsService = (IGameStateService)game.Services.GetService(typeof(IGameStateService));
            gsService.MonsterAdded += new EventHandler(gsService_MonsterAdded);
            gsService.ChangeActiveButtons += new EventHandler(ChangeActiveButtonsHandler);
            this.gameState = gsService.GameState;
            base.Initialize();
        }

        void gsService_MonsterAdded(object sender, EventArgs e)
        {
            SetupMonsterDisplays();
        }

        public override void LoadContent()
        {
            background = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "gamescreen-background");
            hexoutline = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "hexagon-outline");
            hexshadow = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "hexagon-shadow");
            flag = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "flag");
            SetupHexDisplays();
            SetupMonsterDisplays();
            SetupScrollButtons();
            gameScreenSize = new Rectangle((int)(ScrollLeftButton.Origin.X + ScrollLeftButton.Sprite.Width),
                (int)(ScrollUpButton.Origin.Y + ScrollUpButton.Sprite.Height),
                (int)(ScrollRightButton.Origin.X - ScrollRightButton.Sprite.Width),
                (int)(ScrollDownButton.Origin.Y - ScrollDownButton.Sprite.Height));

            infoBox.LoadContent(game.Content);

            //see ControlPanel.cs
            LoadControlPanelContent();

            base.LoadContent();
        }
        /// <summary>
        /// Helper for LoadContent(). Instantiates the HexDisplay objects in HexDisplays.
        /// </summary>
        private void SetupHexDisplays()
        {
            try
            {
                Texture2D hexSprite = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "equilateral-hexagon");
                int startX = 15;
                int startY = 15;

                foreach (Hex hex in gameState.Hexes)
                {
                    int x = (int)Math.Floor(startX + hex.Col * (hexSprite.Width * 0.75));
                    int y = (int)Math.Ceiling((double)startY + hex.Row * hexSprite.Height);

                    if (hex.Col % 2 == 1)
                    {
                        y += (int)Math.Ceiling(hexSprite.Height / 2.0);
                    }
                    HexDisplays.Add(new HexDisplay(hex, x, y, hexSprite));
                }
            }
            catch (System.IO.FileNotFoundException fnfe)
            {
                ExceptionPolicy.HandleException(fnfe, "MIMP Exception Policy");
            }
        }
        /// <summary>
        /// Helper for LoadContent(). Instantiates the MonsterDisplay objects in MonsterDisplays.
        /// </summary>
        private void SetupMonsterDisplays()
        {
            foreach (Monster monster in gameState.Monsters)
            {
                MonsterDisplay md;
                Texture2D mSprite = null;
                try
                {
                    mSprite = game.Content.Load<Texture2D>(MIMPGame.MONSTER_SPRITES_DIR + monster.IconName);

                }
                catch (System.IO.FileNotFoundException)
                {
                    mSprite = game.Content.Load<Texture2D>(MIMPGame.MONSTER_SPRITES_DIR + "Default");
                }
                finally
                {
                    Texture2D copy = new Texture2D(game.GraphicsDevice, mSprite.Width, mSprite.Height, mSprite.LevelCount, mSprite.TextureUsage, mSprite.Format);
                    Color[] arr = new Color[mSprite.Width * mSprite.Height];
                    mSprite.GetData<Color>(arr);
                    copy.SetData<Color>(arr);
                    md = new MonsterDisplay(monster, 1, copy);
                    md.ReplaceColorInSprite(Color.White, monster.Controller.Color);
                    MonsterDisplays.Add(md);
                }
            }

        }
        private void SetupScrollButtons()
        {
            Texture2D scrUp = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "scrollbar-up");
            Texture2D scrDown = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "scrollbar-down");
            Texture2D scrLeft = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "scrollbar-left");
            Texture2D scrRight = game.Content.Load<Texture2D>(MIMPGame.GAMEPLAY_SCREEN_SPRITES_DIR + "scrollbar-right");
            ScrollUpButton = new UIButtonDisplay(0, 0, 1, scrUp, Keys.Up, UIButtons.ScrollUp);
            ScrollDownButton = new UIButtonDisplay(0, MIMPGame.WIDOW_HEIGHT * 3 / 4 - scrDown.Height, 1, scrDown, Keys.Down, UIButtons.ScrollDown);
            ScrollLeftButton = new UIButtonDisplay(0, 0, 1, scrLeft, Keys.Left, UIButtons.ScrollLeft);
            ScrollRightButton = new UIButtonDisplay(MIMPGame.WINDOW_WIDTH - scrRight.Width, 0, 1, scrRight, Keys.Right, UIButtons.ScrollRight);
        }

        public override void HandleInput(InputState input) {
            MouseState curMouseState = input.CurrentMouseState;
            KeyboardState curKeyState = input.CurrentKeyboardStates[0];

            ScrollDownButton.SetIsMouseOver(curMouseState);
            ScrollUpButton.SetIsMouseOver(curMouseState);
            ScrollLeftButton.SetIsMouseOver(curMouseState);
            ScrollRightButton.SetIsMouseOver(curMouseState);

            if (curKeyState.IsKeyDown(Keys.Down) ||
                (curMouseState.LeftButton == ButtonState.Pressed && ScrollDownButton.IsMouseOver))
            {
                ScrollHexes(UIButtons.ScrollDown, 4);
            }
            else if (curKeyState.IsKeyDown(Keys.Up) ||
                (curMouseState.LeftButton == ButtonState.Pressed && ScrollUpButton.IsMouseOver))
            {
                ScrollHexes(UIButtons.ScrollUp, 4);
            }
            else if (curKeyState.IsKeyDown(Keys.Right) ||
                (curMouseState.LeftButton == ButtonState.Pressed && ScrollRightButton.IsMouseOver))
            {
                ScrollHexes(UIButtons.ScrollRight, 4);
            }
            else if (curKeyState.IsKeyDown(Keys.Left) ||
                (curMouseState.LeftButton == ButtonState.Pressed && ScrollLeftButton.IsMouseOver))
            {
                ScrollHexes(UIButtons.ScrollLeft, 4);
            }

            infoBox.IsVisible = false;

            //Set IsMouseOver for all HexDisplays.
            foreach (HexDisplay display in HexDisplays)
            {
                display.IsClickable = display.IsInRectangle(gameScreenSize);
                display.SetIsMouseOver(curMouseState);
                if (display.IsMouseOver && curMouseState.RightButton == ButtonState.Pressed)
                {
                    infoBox.IsVisible = true;
                    if (display.Hex.IsMonsterOn)
                    {
                        infoBox.Origin = display.Origin + new Vector2(display.Sprite.Width * 3 / 4, display.Sprite.Height / 2);
                        Monster m = display.Hex.MonsterOn;
                        infoBox.Text = display.Hex.ToString();
                        infoBox.Text += "\n\n" + m.GetMonsterInfoString(GameState.Phases.Play);
                    }
                    else
                    {
                        infoBox.Origin = display.Origin + new Vector2(display.Sprite.Width / 2, display.Sprite.Height / 2);
                        infoBox.Text = display.Hex.ToString();
                    }
                }
            }

            //Check for left clicks...
            //TODO: add check to see if mouse is somewhere over the hex map (do not bother doing all this stuff if it is not)
            if (input.IsNewLeftMouseClick())
            {
                HexDisplay clicked = HexDisplays.Find(delegate(HexDisplay hd) { return hd.IsMouseOver; });
                if (clicked != null)
                {
                    if (HexLeftClicked != null)
                    {
                        ConsoleModifiers mods = GetModifiers(input.CurrentKeyboardStates[0]);
                        HexLeftClicked(this, new HexClickEventArgs(clicked.Hex, mods));
                    }
                }
            }
            //...and do the same for right clicks
            else if (input.IsNewRightMouseClick())
            {
                HexDisplay clicked = HexDisplays.Find(delegate(HexDisplay hd) { return hd.IsMouseOver; });
                if (clicked != null)
                {
                    if (HexRightClicked != null)
                    {
                        ConsoleModifiers mods = GetModifiers(input.CurrentKeyboardStates[0]);
                        HexRightClicked(this, new HexClickEventArgs(clicked.Hex, mods));
                    }
                }
            }
            //see ControlPanel.cs
            HandleControlPanelInput(input);
        }
        /// <summary>
        /// Changes the Origin of each HexDisplay.
        /// </summary>
        /// <param name="dir">The direction to scroll.</param>
        /// <param name="delta">The number of pixels to scroll each HexDisplay.</param>
        void ScrollHexes(UIButtons dir, float delta)
        {
            foreach (HexDisplay display in HexDisplays)
            {
                switch (dir)
                {
                    case UIButtons.ScrollUp:
                        display.Origin += new Vector2(0, delta);
                        break;
                    case UIButtons.ScrollDown:
                        display.Origin += new Vector2(0, -delta);
                        break;
                    case UIButtons.ScrollRight:
                        display.Origin += new Vector2(-delta, 0);
                        break;
                    case UIButtons.ScrollLeft:
                        display.Origin += new Vector2(delta, 0);
                        break;
                }
            }
        }

        /// <summary>
        /// This is called when the DrawableGameComponent should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Draw(GameTime gameTime)
        {
            ScreenManager.SpriteBatch.Begin();
            ScreenManager.SpriteBatch.Draw(background, new Vector2(0, 0), Color.White);

            foreach (HexDisplay display in HexDisplays)
            {
                Rectangle drawableArea = new Rectangle(gameScreenSize.Left - display.Sprite.Width,
                    gameScreenSize.Top - display.Sprite.Height, gameScreenSize.Right + display.Sprite.Width * 2,
                    gameScreenSize.Bottom + display.Sprite.Height * 2);
                if (display.IsInRectangle(drawableArea))
                {
                    ScreenManager.SpriteBatch.Draw(display.Sprite, display.Bounds, display.TerrainColor);

                    if (display.Hex.IsMonsterOn)
                    {
                        MonsterDisplay monsterDisplay = MonsterDisplays.Find(
                            delegate(MonsterDisplay md) { return md.Monster.Equals(display.Hex.MonsterOn); });
                        int centerX = (int)display.Origin.X + (display.Sprite.Width - monsterDisplay.Sprite.Width) / 2;
                        int centerY = (int)display.Origin.Y + (display.Sprite.Height - monsterDisplay.Sprite.Height) / 2;
                        monsterDisplay.Origin = new Vector2(centerX, centerY);
                        ScreenManager.SpriteBatch.Draw(monsterDisplay.Sprite, monsterDisplay.Bounds, Color.White);
                    }
                    if (display.Hex.HasControlPoint)
                    {
                        int centerX = (int)display.Origin.X + (display.Sprite.Width - flag.Width) / 2;
                        int centerY = (int)display.Origin.Y - 2;
                        Color cpColor = display.Hex.ControlPoint.Controller != null ? display.Hex.ControlPoint.Controller.Color : Color.Gray;
                        ScreenManager.SpriteBatch.Draw(flag, new Vector2(centerX, centerY), cpColor);
                    }
                }
            }

            HexDisplay hovering = HexDisplays.Find(delegate(HexDisplay d) { return d.IsMouseOver; });
            if (hovering != null)
            {
                ScreenManager.SpriteBatch.Draw(hexshadow, hovering.Bounds, Color.White);
            }
            //foreach (Player p in gameState.Players)
            //{
            //    List<HexDisplay> monstersControlled = HexDisplays.FindAll(delegate(HexDisplay d) { return d.Hex.IsMonsterOn && (d.Hex.MonsterOn.Controller == p); });
            //    if (monstersControlled != null)
            //    {
            //        foreach (HexDisplay hd in monstersControlled)
            //        {
            //            ScreenManager.SpriteBatch.Draw(hexoutline, hd.Bounds, p.Color);
            //        }
            //    }
            //}
            foreach(Monster m in gameState.SelectedMonsters)
            {
                HexDisplay selected = HexDisplays.Find(delegate(HexDisplay d) { return d.Hex.Equals(m.Location); });
                if (selected != null)
                {
                    ScreenManager.SpriteBatch.Draw(hexoutline, selected.Bounds, Color.Silver);
                }
            }
            foreach (Monster m in gameState.DefendingMonsters)
            {
                HexDisplay selected = HexDisplays.Find(delegate(HexDisplay d) { return d.Hex.Equals(m.Location); });
                if (selected != null)
                {
                    ScreenManager.SpriteBatch.Draw(hexoutline, selected.Bounds, m.Controller.Color);
                }
            }
           
            ScreenManager.SpriteBatch.Draw(ScrollDownButton.Sprite, ScrollDownButton.Bounds, Color.White);
            ScreenManager.SpriteBatch.Draw(ScrollUpButton.Sprite, ScrollUpButton.Bounds, Color.White);
            ScreenManager.SpriteBatch.Draw(ScrollRightButton.Sprite, ScrollRightButton.Bounds, Color.White);
            ScreenManager.SpriteBatch.Draw(ScrollLeftButton.Sprite, ScrollLeftButton.Bounds, Color.White);

            ScreenManager.SpriteBatch.End();

            //see ControlPanel.cs
            DrawControlPanel(gameTime);
        }
    }
}